

#define FREEGLUT_STATIC
#include <GL/glut.h>
#include <GL/freeglut_ext.h>

//models for our particles

//airplane - num points: 8
static const GLfloat airplane[] =
{
    // Positions
    -5.0f, 1.0f, 0.0f,
    -1.0f, 1.5f, 0.0f,
    -1.0f, 1.5f, 7.0f,
    0.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 10.0f,
    1.0f, 1.5f, 0.0f,
    1.0f, 1.5f, 7.0f,
    5.0f, 1.0f, 0.0f,

    // Normals
    0.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 0.0f,
};


extern GLuint GRAVITY_PARTICLES;
extern GLuint FLOCK_SIZE;
extern GLuint FIREWORK_PARTICLES;

//customizable parameters
static float DISTANCE_FROM_SYSTEM = 2000.0f;
static const GLfloat* PARTICLE_MODEL = airplane;
static const uint PARTICLE_MODEL_SIZE = sizeof(airplane);
static const uint PARTICLE_MODEL_NUM_POINTS = 8;
static const float FIELD_OF_VIEW = 35.0f;
static const float ASPECT_RATIO = 800.0f / 600.0f;

extern float MAX_INITIAL_GRAVITY_SPEED;
static float goal_position[] = {0.0f,0.0f,0.0f};
static float goal_color[] = {0.0f,0.0f,0.0f};
static bool mouse_clicked = false;
static bool mouse_in_screen = false;
extern float trail_length;

extern float rule1_weight;
extern float rule2_weight;
extern float rule3_weight;

extern float colour_rule1_weight;
extern float colour_rule2_weight;
extern float colour_rule3_weight;

extern float closest_allowed_distance;
extern float closest_allowed_colour;

extern float rule_speed;

extern float colour_rule_speed;

extern float firework_fuzziness_increment;

extern float closest_allowed_distance_speed;

extern float closest_allowed_colour_speed;

// These are the default window sizes. If the window 
// is resized, this needs to be resized or the cursor 
// position will be off
extern int WINDOW_WIDTH;
extern int WINDOW_HEIGHT;

//types and particle files
enum ParticleType 
{
	FLOCKING = 0,
	FIREWORKS = 1,
	GRAVITY = 2,
};

extern ParticleType particle_type;

static const char* particle_files[] = 
{
	"./src/flocking.vs",
	"./src/fireworks.vs",
	"./src/gravity.vs",
};


extern int SHADER_INDEX;
static int SHADER_COUNT = 12;

static int POINTILISM_SHADER = 0;
static int RAINBOW_SHADER = 1;
static int POSITION_SHADER = 2;
static int VELOCITY_SHADER = 3;
static int SHALLOW_SHADER = 4;
static int BUBBLES_SHADER = 5;
static int LSD_SHADER = 6;
static int TECHNOBABBLE_SHADER = 7;
static int AIRPLANES_SHADER = 8;
static int COLORFLOCK_SHADER = 9;
static int GRAVMASS_SHADER = 10;
static int FIREWORKS_SHADER = 11;


static const char* VERTEX_SHADERS[] = 
{
	"./src/shaders/pointilism.vs",
	"./src/shaders/rainbows.vs",
	"./src/shaders/position.vs",
	"./src/shaders/velocity.vs",
	"./src/shaders/shallow.vs",
	"./src/shaders/bubbles.vs",
	"./src/shaders/lsd.vs",
	"./src/shaders/technobabble.vs",
	"./src/shaders/airplanes.vs",
	"./src/shaders/colorFlock.vs",
	"./src/shaders/gravMass.vs",
	"./src/shaders/fireworks.vs"
};

static const char* GEOMETRY_SHADERS[] = 
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"./src/shaders/airplanes.gs",
	NULL,
	NULL,
	NULL
};

static const char* FRAGMENT_SHADERS[] = 
{
	"./src/shaders/pointilism.fs",
	"./src/shaders/rainbows.fs",
	"./src/shaders/position.fs",
	"./src/shaders/velocity.fs",
	"./src/shaders/shallow.fs",
	"./src/shaders/bubbles.fs",
	"./src/shaders/lsd.fs",
	"./src/shaders/technobabble.fs",
	"./src/shaders/airplanes.fs",
	"./src/shaders/colorFlock.fs",
	"./src/shaders/gravMass.fs",
	"./src/shaders/fireworks.fs"
};

//camera stuff
extern GLFrame view_frame;
extern GLFrustum view_frustum;
static GLMatrixStack modelview_mat;
static GLMatrixStack projection_mat;
static GLGeometryTransform transform_pipeline;

//update program variables
static GLuint update_program;
extern GLuint update_vao[2];
static GLuint update_vs;

//render program variables
extern GLuint render_program;
static GLuint render_vao[2];
static GLuint render_vs;
static GLuint render_gs;
static GLuint render_fs;

//particle positions
extern GLuint particle_position[2];
static GLuint position_tbo[2];

//particle velocities
extern GLuint particle_velocity[2];
static GLuint velocity_tbo[2];

//gravity-specific stuff
extern GLuint particle_mass[2];
static GLuint mass_tbo[2];

//color-specific stuff
extern GLuint particle_color[2];
static GLuint color_tbo[2];
extern GLuint particle_colorVelocity[2];
static GLuint colorVelocity_tbo[2];

//particle geometry
static GLuint particle_geometry;

//fireworks color
extern GLuint firework_colors;

//particle sprite
static GLuint particle_sprite;
static GLint texture_loc;

//base sprite size and depth divisor
extern GLuint base_size_loc;
extern GLuint depth_divisor_loc;
extern GLint base_size;
extern float depth_divisor;

//locations for timestep, goal, model-view-projection matrix
static GLint timestep_loc;
static GLint grav_const_loc;
static GLint goal_loc;
static GLint goalColor_loc;
extern GLint mvp_loc;
static GLint mass_loc;
static GLint toggle_loc;

static GLint rule1_loc;
static GLint rule2_loc;
static GLint rule3_loc;
static GLint colour_rule1_loc;
static GLint colour_rule2_loc;
static GLint colour_rule3_loc;
static GLint closest_dist_loc;
static GLint closest_colour_loc;

//variable declarations
extern GLuint frame_index;
static CStopWatch stopwatch;
static float last_update_time = 0.0f;

extern float timestep_mult;
extern float timestep_mult_step;

//transform feedback outputs
static const GLchar * tf_flocking_outputs[] = { "position_out", "velocity_out", "color_out", "colorVelocity_out"};
static const GLchar * tf_fireworks_outputs[] = { "position_out", "velocity_out"};
static const GLchar * tf_gravity_outputs[] = { "position_out", "velocity_out", "mass_out"};

//true if we're ready to exit
static bool done = false;

//random number generator
//generates a float between 0 and 1
static inline float random_float(){
    return (float)rand()/(float)RAND_MAX;
}

//generate initial positions and velocities
void init_particles(float* positions, unsigned int num_positions, float scale, const float *bias);

void output_string(int x, int y);

//Initialization for the generic system
void initGeneric();

//render the scene
void render_scene(void);

//does any necessary cleanup before the program shuts down
void cleanup(void);

//handles changing the size of the window
void change_size(int w, int h);

void update_render();

//entry point for the program
//calls all necessary initialization functions and sturts glut's main loop
int main(int argc, char* argv[]);
